Skip to content

fix(lint): consolidate five more private "Did you mean?" copies onto suggestName - #14757

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-14577-suggest-copies-consolidation
Sep 3, 2026
Merged

fix(lint): consolidate five more private "Did you mean?" copies onto suggestName#14757
baozhoutao merged 2 commits into
mainfrom
claude/issue-14577-suggest-copies-consolidation

Conversation

@claude

@claude claude Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #14577

Summary

Follow-up to #14268/#14575: five more validate-*.ts rules in packages/lint/src carried a private suggest/distance pair, re-deriving the same edit-distance-only "Did you mean?" budget that object-graph.ts already exports as suggestName. This PR consolidates all five onto the shared helper.

  • Tier 1validate-action-name-refs.ts, validate-chart-bindings.ts, validate-searchable-fields.ts: delete the private distance/suggest pair, import suggestName from ./object-graph.js.
  • Tier 2validate-ai-tool-references.ts, validate-translation-references.ts: keep the rule's own one-line pre-pass (the action_NAME tool-family prefix, and a snake_case namespace-segment match — rule-local knowledge, not the shared helper's business), then delegate the fallback to suggestName instead of a private Levenshtein copy.

object-graph.ts's helper is untouched (#14268 already ruled it — this card consumes it, does not revise it). validate-react-page-props.ts (nearestKnown, fixed budget of at most 2) and validate-rule-schema-formats.ts (nearestRegisteredFormat, exported, case-insensitive, alphabetical tie-break) stay fenced out — both are a different contract on purpose per #14577's triage, and this PR does not touch them.

Population grep (base 4d0d9445a, re-verified at final head 7a17f3bf1)

$ git grep -n "function distance\|function suggest\|function levenshtein" -- packages/lint/src
packages/lint/src/object-graph.ts:399:function distance(...)                    ← untouched, the shared helper's own internals
packages/lint/src/validate-action-name-refs.ts:84/101      ← Tier 1, deleted
packages/lint/src/validate-ai-tool-references.ts:70/87      ← Tier 2, deleted
packages/lint/src/validate-chart-bindings.ts:83/100         ← Tier 1, deleted
packages/lint/src/validate-react-page-props.ts:...editDistance/nearestKnown  ← fenced, untouched
packages/lint/src/validate-rule-schema-formats.ts:...editDistance            ← fenced, untouched
packages/lint/src/validate-searchable-fields.ts:275/289     ← Tier 1, deleted
packages/lint/src/validate-translation-references.ts:159/186 ← Tier 2, deleted

After this PR, function distance is gone from all five files (Tier 1 has zero function distance/suggest left at all; Tier 2 keeps a thin function suggest(...) that is now a one-line pre-pass + return suggestName(...), not a duplicated algorithm):

$ git grep -n "function distance\|function suggest\b" -- packages/lint/src
packages/lint/src/object-graph.ts:399:function distance(...)
packages/lint/src/validate-ai-tool-references.ts:72:function suggest(...)       ← thin wrapper, delegates to suggestName
packages/lint/src/validate-translation-references.ts:173:function suggest(...) ← thin wrapper, delegates to suggestName

Per-site before/after (containment pre-pass now reachable)

The shared helper's containment pre-pass (a candidate that contains the target, or vice versa, scores ahead of any edit-distance match) is now every one of these five rules' behaviour too — it only ever adds a hint where the private copy's budget previously gave none, matching #14268/#14575's precedent.

Site Before (private copy) After (suggestName)
validate-action-name-refs.ts archive(no hint, 17 edits > budget 2) archiveDid you mean "archive_completed_deals"?
validate-chart-bindings.ts amount(no hint, 4 edits > budget 2) — the issue's own headline example amountDid you mean "sum_amount"?
validate-searchable-fields.ts amount(no hint, 4 edits > budget 2) amountDid you mean "sum_amount"?
validate-ai-tool-references.ts prefix pre-pass unchanged (still wins first); a miss with no prefix match → (no hint) prefix pre-pass unchanged; a miss now also reaches containment: knowledge_baseDid you mean "search_knowledge_base"?
validate-translation-references.ts segment pre-pass unchanged (still wins first); a miss with no segment match → (no hint) segment pre-pass unchanged; a miss now also reaches containment: amountDid you mean "amountsummary"?

Existing "Did you mean?" assertions in each file's test suite were re-run and are unchanged (the containment pre-pass never overrides an existing distance-based winner in these fixtures — verified by running each file's test suite before and after). One new pin test was added per file (5 total) covering exactly this containment class — the amountsum_amount shape the issue names, or its Tier-2 fallback-after-pre-pass-miss equivalent.

Fenced files / helper untouched

$ git diff origin/main --stat -- packages/lint/src/object-graph.ts packages/lint/src/validate-react-page-props.ts packages/lint/src/validate-rule-schema-formats.ts
(empty)

git diff origin/main --stat (final head 7a17f3bf1)

 .../lint/src/validate-action-name-refs.test.ts     | 15 ++++++++
 packages/lint/src/validate-action-name-refs.ts     | 34 ++----------------
 .../lint/src/validate-ai-tool-references.test.ts   | 16 +++++++++
 packages/lint/src/validate-ai-tool-references.ts   | 34 +++---------------
 packages/lint/src/validate-chart-bindings.test.ts  | 30 ++++++++++++++++
 packages/lint/src/validate-chart-bindings.ts       | 38 +++-----------------
 .../lint/src/validate-searchable-fields.test.ts    | 19 ++++++++++
 packages/lint/src/validate-searchable-fields.ts    | 35 ++-----------------
 .../src/validate-translation-references.test.ts    | 16 +++++++++
 .../lint/src/validate-translation-references.ts    | 40 +++++-----------------
 10 files changed, 117 insertions(+), 160 deletions(-)
 + .changeset/lint-suggest-name-consolidation-tier2.md (new)

Tests

  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 against the five test files — 164 passed, 0 failed (5 files).
  • pnpm --filter @objectstack/lint test (full package suite, includes the 5 new pin tests) — 93 test files passed, 2841 passed / 5 skipped (2846), run twice: once at merge-base 4d0d9445a, once at final head 7a17f3bf1 — identical results both times.
  • pnpm --filter @objectstack/lint typecheck — clean (pre-existing test-typecheck-debt.json entries unrelated to this change, unchanged: 2 file(s)/6 error(s)/2 pinned signatures, shrink-only, @ts-expect-error 退役 pin 在 packages/spec 里是幽灵检查:tsconfig 把 **/*.test.ts 排除出唯一的 tsc --noEmit #5286).
  • Dependency closure built first: pnpm --filter '@objectstack/lint^...' build (spec, formula) before any test run.

Gates

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands against the five files plus the changeset derived 28 command(s). All 28 run at final head 7a17f3bf1:

  • 26/28 exit 0.
  • node scripts/check-test-completeness.mjs — exit 3, self-declared NOT MEASURED ("nothing was measured... pass a saved turbo run test log, or record this gate as NOT MEASURED" — needs a turbo run test log this local run does not have).
  • pnpm check:dual-build-cjs-loads — exit 3, self-declared PREREQUISITE NOT MET ("this gate reads built output, and some package has no dist/" — needs a full repo-wide pnpm build across ~79 packages, out of scope for a targeted local run; CI's build step covers it).

Both are explicit self-reports, not failures — captured with exit code read before any pipe.

Merged origin/main twice during this run (once at 5258b63f8, once at 7a17f3bf1, both clean fast-forwards, neither touched packages/lint/) — the final test+typecheck pair was re-run at 7a17f3bf1 to confirm the result still holds at the pushed head.

Scope

File surface: the five rule files + their test files + one changeset — exactly the claimed surface, nothing else touched. @objectstack/lint patch changeset included, describing the hint-quality change per rule.


Generated by Claude Code

…suggestName

Follow-up to #14268/#14575. Five more validate-*.ts rules in packages/lint
carried a private suggest/distance pair, byte-for-byte re-deriving the
edit-distance-only budget object-graph.ts already exports as suggestName.

Tier 1 (validate-action-name-refs, validate-chart-bindings,
validate-searchable-fields): delete the private pair, import suggestName.

Tier 2 (validate-ai-tool-references, validate-translation-references): keep
the rule's own one-line pre-pass (the action_<name> tool-family prefix, and a
snake_case namespace-segment match — rule-local knowledge), then delegate the
fallback to suggestName instead of a private Levenshtein copy.

object-graph.ts's helper is untouched (#14268 already ruled it).
validate-react-page-props.ts and validate-rule-schema-formats.ts stay fenced
out — both are a different contract on purpose per #14577's triage.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@github-actions github-actions Bot added the size/m label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json df657d9df1fac5a2222f183008532613a1df585fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 5e4bfc72ff51e3e3bdbc61ddd9beb0455065d529 — the merge of head e9e0afa1bf414a8d59107fca2023b64bb180b405 into base df657d9df1fac5a2222f183008532613a1df585f, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5e4bfc72ff51e3e3bdbc61ddd9beb0455065d529 && git checkout 5e4bfc72ff51e3e3bdbc61ddd9beb0455065d529
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin df657d9df1fac5a2222f183008532613a1df585f e9e0afa1bf414a8d59107fca2023b64bb180b405 && git checkout -B drift-repro df657d9df1fac5a2222f183008532613a1df585f && git merge --no-ff e9e0afa1bf414a8d59107fca2023b64bb180b405

node scripts/docs-audit/affected-docs.mjs --json df657d9df1fac5a2222f183008532613a1df585f

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@baozhoutao
baozhoutao added this pull request to the merge queue Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33706085300 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
      ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 37 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33706452985 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test:  FAIL   integration  test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
      ↳ 失败原因: @objectstack/cli:test: AssertionError: expected 'SIGKILL' to be null
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 41 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit 3a5b8c9 Sep 3, 2026
34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-14577-suggest-copies-consolidation branch September 3, 2026 02:51
This was referenced Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] five more private suggest/distance ("Did you mean?") copies remain in packages/lint after #14268 consolidated the ruled three

2 participants